# main.py
#
# Copyright 2025 mirkobrombin <brombin94@gmail.com>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, in version 3 of the License.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>.
#

import gettext
import locale
import sys
import webbrowser
from os import path

import gi

from bottles.backend.health import HealthChecker
from bottles.backend.logger import Logger
from bottles.frontend.params import (
    APP_ID,
    APP_MAJOR_VERSION,
    APP_MINOR_VERSION,
    APP_VERSION,
)

gi.require_version("Gtk", "4.0")
gi.require_version("Adw", "1")
gi.require_version("GtkSource", "5")
gi.require_version("Xdp", "1.0")
# gi.require_version("XdpGtk4", "1.0")

# ruff: noqa: E402
from gi.repository import Adw, Gio, GLib, GObject  # type: ignore

from bottles.frontend.views.preferences import PreferencesWindow
from bottles.frontend.windows.window import BottlesWindow

logging = Logger()

# region Translations
"""
This code snippet searches for and uploads translations to different
directories, depending on your production or development environment.
The function _() can be used to create and retrieve translations.
"""
share_dir = path.join(sys.prefix, "share")
base_dir = "."

if getattr(sys, "frozen", False):
    base_dir = path.dirname(sys.executable)
    share_dir = path.join(base_dir, "share")
elif sys.argv[0]:
    exec_dir = path.dirname(path.realpath(sys.argv[0]))
    base_dir = path.dirname(exec_dir)
    share_dir = path.join(base_dir, "share")

    if not path.exists(share_dir):
        share_dir = base_dir

locale_dir = path.join(share_dir, "locale")

if not path.exists(locale_dir):  # development
    locale_dir = path.join(base_dir, "build", "mo")

locale.bindtextdomain("bottles", locale_dir)
locale.textdomain("bottles")
gettext.bindtextdomain("bottles", locale_dir)
gettext.textdomain("bottles")
_ = gettext.gettext


# endregion


class Bottles(Adw.Application):
    arg_exe = None
    arg_bottle = None
    dark_provider = None
    journal_window = None

    def __init__(self):
        super().__init__(
            application_id=APP_ID,
            resource_base_path="/com/usebottles/bottles",
            flags=Gio.ApplicationFlags.HANDLES_COMMAND_LINE,
            register_session=True,
        )
        self.__create_action("quit", self.__quit, ["<primary>q", "<primary>w"])
        self.__create_action("about", self.__show_about_dialog)
        self.__create_action("import", self.__show_importer_view, ["<primary>i"])
        self.__create_action("journal", self.__show_journal, ["<primary>j"])
        self.__create_action("preferences", self.__show_preferences, ["<primary>comma"])
        self.__create_action("help", self.__help, ["F1"])
        self.__create_action("new", self.__new_bottle, ["<primary>n"])

        self.__register_arguments()

    def __register_arguments(self):
        """
        This function registers the command line arguments.
            --version, -v: Prints the version of the application.
            --executable, -e: The path of the executable to be launched.
            --lnk, -l: The path of the shortcut to be launched.
            --bottle, -b: The name of the bottle to be used.
            --arguments, -a: The arguments to be passed to the executable.
            --help, -h: Prints the help.
        """
        self.add_main_option(
            "version",
            ord("v"),
            GLib.OptionFlags.NONE,
            GLib.OptionArg.NONE,
            _("Show version"),
            None,
        )
        self.add_main_option(
            "executable",
            ord("e"),
            GLib.OptionFlags.NONE,
            GLib.OptionArg.STRING,
            _("Executable path"),
            None,
        )
        self.add_main_option(
            "lnk",
            ord("l"),
            GLib.OptionFlags.NONE,
            GLib.OptionArg.STRING,
            _("lnk path"),
            None,
        )
        self.add_main_option(
            "bottle",
            ord("b"),
            GLib.OptionFlags.NONE,
            GLib.OptionArg.STRING,
            _("Bottle name"),
            None,
        )
        self.add_main_option(
            "arguments",
            ord("a"),
            GLib.OptionFlags.NONE,
            GLib.OptionArg.STRING,
            _("Pass arguments"),
            None,
        )
        self.add_main_option(
            GLib.OPTION_REMAINING,
            0,
            GLib.OptionFlags.NONE,
            GLib.OptionArg.STRING_ARRAY,
            "URI",
            None,
        )

    def do_command_line(self, command):
        """
        This function is called when the application is launched from the
        command line. It parses the command line arguments and calls the
        corresponding functions.
        See: __register_arguments()
        """
        commands = command.get_options_dict()

        if commands.contains("executable"):
            self.arg_exe = commands.lookup_value("executable").get_string()

        if commands.contains("version"):
            print(APP_VERSION)
            quit()

        if commands.contains("bottle"):
            self.arg_bottle = commands.lookup_value("bottle").get_string()

        if not self.arg_exe:
            """
            If no executable is specified, look if it was passed without
            the --executable argument.
            """
            for a in sys.argv:
                if a.endswith((".exe", ".msi", ".bat", ".lnk")):
                    self.arg_exe = a
                    logging.info(
                        _("Launching with executable: {0}").format(a),
                    )

        uri = commands.lookup_value(GLib.OPTION_REMAINING)
        logging.info(
            _("Launching with URI: {0}").format(uri),
        )
        if uri:
            return self.__process_uri(uri)

        self.do_activate()
        return 0

    def __process_uri(self, uri):
        """
        This function processes the URI passed to the application.
        e.g. xdg-open bottles:run/<bottle>/<program>
        """
        uri = uri[0]

        if uri.startswith("bottles:run/"):
            if len(uri.split("/")) != 3:
                logging.error(_("Invalid URI (syntax: bottles:run/<bottle>/<program>)"))
                return False

            uri = uri.replace("bottles:run/", "")
            bottle, program = uri.split("/")

            import subprocess

            subprocess.Popen(["bottles-cli", "run", "-b", bottle, "-p", program])
            return 0

        try:
            from bottles.frontend.windows.bottlepicker import BottlePickerDialog

            dialog = BottlePickerDialog(application=self, arg_exe=uri)
            dialog.present()
            return 0
        except Exception as e:
            logging.error(
                _("Error while processing URI: {0}").format(e),
            )
            return False

    def do_startup(self):
        """
        This function is called when the application is started.
        Here we register the application actions (shortcuts).
        See: __register_actions()
        """
        Adw.Application.do_startup(self)

    def do_activate(self):
        """
        This function is called when the application is activated.
        """

        logging.info(
            _("[Activate] request received."),
        )

        # create the main window
        Adw.Application.do_activate(self)
        win = self.props.active_window
        if not win:
            win = BottlesWindow(application=self, arg_bottle=self.arg_bottle)
        self.win = win

        win.present()

    def __quit(self, *args):
        """
        This function close the application.
        It is used by the [Ctrl+Q] shortcut.
        """
        logging.info(
            _("[Quit] request received."),
        )
        self.win.on_close_request()
        quit()

    @staticmethod
    def __help(action=None, param=None):
        """
        This function open the documentation in the user's default browser.
        It is used by the [F1] shortcut.
        """
        logging.info(
            _("[Help] request received."),
        )
        webbrowser.open_new_tab("https://docs.usebottles.com")

    def __refresh(self, action=None, param=None):
        """
        This function refresh the user bottle list.
        It is used by the [Ctrl+R] shortcut.
        """
        logging.info(
            _("[Refresh] request received."),
        )
        self.win.manager.update_bottles()

    def __show_preferences(self, *args):
        preferences_window = PreferencesWindow(self.win)
        preferences_window.present()

    def __new_bottle(self, *args):
        self.win.show_add_view()

    def __show_importer_view(self, widget=False, *args):
        self.win.main_leaf.set_visible_child(self.win.page_importer)

    def __show_journal(self, *args):
        from bottles.frontend.windows.journal import JournalDialog

        if self.journal_window:
            self.journal_window.present()
            return

        self.journal_window = JournalDialog(application=self)
        self.journal_window.set_transient_for(self.win)
        self.journal_window.connect("close-request", self.__on_journal_close)
        self.journal_window.present()

    def __on_journal_close(self, *_args):
        self.journal_window = None
        return False

    def __show_about_dialog(self, *_args):
        developers = [
            "Mirko Brombin https://github.com/mirkobrombin",
            "hthre7 https://github.com/hthre7",
            "Kekun https://github.com/Kekun",
            "Sonny Piers https://github.com/sonnyp",
            "BrainBlasted https://github.com/BrainBlasted",
            "Francesco Masala <mail@francescomasala.me>",
            "Hari Rana (TheEvilSkeleton) https://theevilskeleton.gitlab.io",
            "axtlos https://axtloss.github.io",
            "Oro https://github.com/orowith2os",
            "gregorni https://gitlab.com/gregorni",
        ]

        artists = [
            "Marco Montini https://github.com/marckniack",
            "Noëlle https://github.com/jannuary",
            "Alvar Lagerlöf https://github.com/alvarlagerlof",
            "Ezekiel Smith https://github.com/ZekeSmith",
        ]

        about_dialog = Adw.AboutDialog.new_from_appdata(
            "/com/usebottles/bottles/appdata",
            f"{APP_MAJOR_VERSION}.{APP_MINOR_VERSION}",
        )
        about_dialog.set_developers(developers)
        about_dialog.set_translator_credits(_("translator_credits"))
        about_dialog.set_artists(artists)
        about_dialog.set_debug_info(HealthChecker().get_results(plain=True))
        about_dialog.add_link(_("Donate"), "https://usebottles.com/funding")
        about_dialog.set_copyright(
            _("Copyright © 2017 {developer_name}").format(
                developer_name=about_dialog.get_developer_name()
            )
        )
        about_dialog.add_acknowledgement_section(
            _("Third-Party Libraries and Special Thanks"),
            [
                "DXVK https://github.com/doitsujin/dxvk",
                "VKD3D https://github.com/HansKristian-Work/vkd3d-proton",
                "DXVK-NVAPI https://github.com/jp7677/dxvk-nvapi",
                "LatencyFleX https://github.com/ishitatsuyuki/LatencyFleX",
                "MangoHud https://github.com/flightlessmango/MangoHud",
                "AMD FidelityFX™ Super Resolution https://www.amd.com/en/technologies/fidelityfx-super-resolution",
                "vkBasalt https://github.com/DadSchoorse/vkBasalt",
                "vkbasalt-cli https://gitlab.com/TheEvilSkeleton/vkbasalt-cli",
                "GameMode https://github.com/FeralInteractive/gamemode",
                "Gamescope https://github.com/Plagman/gamescope",
                "OBS Vulkan/OpenGL capture https://github.com/nowrep/obs-vkcapture",
                "Wine-TKG https://github.com/Frogging-Family/wine-tkg-git",
                "Proton https://github.com/ValveSoftware/proton",
                "Wine-GE https://github.com/GloriousEggroll/wine-ge-custom",
                "Wine https://www.winehq.org",
                "orjson https://github.com/ijl/orjson",
                "libadwaita https://gitlab.gnome.org/GNOME/libadwaita",
                "icoextract https://github.com/jlu5/icoextract",
                "vmtouch https://github.com/hoytech/vmtouch",
                "FVS https://github.com/mirkobrombin/FVS",
                "pathvalidate https://github.com/thombashi/pathvalidate",
            ],
        )
        about_dialog.add_acknowledgement_section(
            _("Sponsored and Funded by"),
            [
                "JetBrains https://www.jetbrains.com/?from=bottles",
                "GitBook https://www.gitbook.com/?ref=bottles",
                "Linode https://www.linode.com/?from=bottles",
                "Appwrite https://appwrite.io/?from=bottles",
                "Community ❤️ https://usebottles.com/funding",
            ],
        )
        about_dialog.present(self.win)

    def __create_action(self, name, callback, shortcuts=None, param=None):
        """Add an application action.

        Args:
            name: the name of the action
            callback: the function to be called when the action is
              activated
            shortcuts: an optional list of accelerators
            param: an optional list of parameters for the action
        """
        action = Gio.SimpleAction.new(name, param)
        action.connect("activate", callback)
        self.add_action(action)
        if shortcuts:
            self.set_accels_for_action(f"app.{name}", shortcuts)


GObject.threads_init()


def main(version):
    app = Bottles()
    return app.run(sys.argv)
